package crossbreed.models.ComparisonModel;

import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import crossbreed.diff.DiffItem;
import crossbreed.diff.IDiff;
import crossbreed.utilities.IErrorLogger;
import crossbreed.utilities.ISimpleFileReader;

public class ComparisonModel implements IComparisonModel
{
	private ISimpleFileReader leftFile;
	private ISimpleFileReader rightFile;
	private IDiff diff;
	private IErrorLogger errorLogger;
	private ArrayList<ComparisonLine> leftLines;
	private ArrayList<ComparisonLine> rightLines;
	private LinkedList<ComparisonModelListener> listeners;

	public ComparisonModel(IDiff diff, IErrorLogger errorLogger, ISimpleFileReader leftFile,
			ISimpleFileReader rightFile)
	{
		listeners = new LinkedList<ComparisonModelListener>();
		this.diff = diff;
		this.errorLogger = errorLogger;
		this.leftFile = leftFile;
		this.rightFile = rightFile;
	}

	@Override
	public void addListener(ComparisonModelListener listener)
	{
		listeners.add(listener);
	}

	@Override
	public ArrayList<ComparisonLine> getLeftLines()
	{
		return leftLines;
	}
	
	@Override
	public ArrayList<ComparisonLine> getRightLines()
	{
		return rightLines;
	}

	@Override
	public String getLeftFilePath()
	{
		return leftFile.getPath();
	}

	@Override
	public void setLeftFilePath(String leftFilePath)
	{
		leftFile.setPath(leftFilePath);
	}

	@Override
	public String getRightFilePath()
	{
		return rightFile.getPath();
	}

	@Override
	public void setRightFilePath(String rightFilePath)
	{
		rightFile.setPath(rightFilePath);
	}

	@Override
	public void createDiff()
	{
		try
		{
			DiffItem[] differences = diff.diffText(leftFile.readAsString(), rightFile.readAsString());
			leftLines = createLeftLines(leftFile.readAllLines(), differences);
			rightLines = createRightLines(rightFile.readAllLines(), differences);
			fireDiffCompleteEvent();
		}
		catch (IOException ioe)
		{
			errorLogger.log(ioe);
			fireDiffFailedEvent(ioe.getMessage());
		}
	}
	
	private ArrayList<ComparisonLine> createRightLines(ArrayList<String> lines, DiffItem[] diffItems)
	{
		int currentLineIndex = 0;
		ArrayList<ComparisonLine> formattedLines = new ArrayList<ComparisonLine>();

		for (DiffItem item : diffItems)
		{
			int numSwaps = Math.min(item.deletedA, item.insertedB);

			while ((currentLineIndex < item.StartB) && (currentLineIndex < lines.size()))
			{
				formattedLines.add(new ComparisonLine(lines.get(currentLineIndex++), LineType.SAME));
			}

			for (int i = 0; i < numSwaps; i++)
			{
				formattedLines.add(new ComparisonLine(lines.get(item.StartB + i), LineType.SWAP));
				currentLineIndex++;
			}

			for (int m = numSwaps; m < item.deletedA; m++)
			{
				formattedLines.add(new ComparisonLine("   ", LineType.INSERT));
			}

			for (int i = numSwaps; i < item.insertedB; i++)
			{
				formattedLines.add(new ComparisonLine(lines.get(item.StartB + i), LineType.DELETE));
				currentLineIndex++;
			}
		}

		while (currentLineIndex < lines.size())
		{
			formattedLines.add(new ComparisonLine(lines.get(currentLineIndex++), LineType.SAME));
		}

		return formattedLines;
	}

	private ArrayList<ComparisonLine> createLeftLines(ArrayList<String> lines, DiffItem[] diffItems)
	{
		int currentLineIndex = 0;
		ArrayList<ComparisonLine> formattedLines = new ArrayList<ComparisonLine>();

		for (DiffItem item : diffItems)
		{
			int numSwaps = Math.min(item.deletedA, item.insertedB);

			while ((currentLineIndex < item.StartA) && (currentLineIndex < lines.size()))
			{
				formattedLines.add(new ComparisonLine(lines.get(currentLineIndex++), LineType.SAME));
			}

			for (int i = 0; i < numSwaps; i++)
			{
				formattedLines.add(new ComparisonLine(lines.get(item.StartA + i), LineType.SWAP));
				currentLineIndex++;
			}

			for (int m = numSwaps; m < item.deletedA; m++)
			{
				formattedLines.add(new ComparisonLine(lines.get(item.StartA + m), LineType.DELETE));
				currentLineIndex++;
			}

			for (int i = numSwaps; i < item.insertedB; i++)
			{
				formattedLines.add(new ComparisonLine("   ", LineType.INSERT));
			}
		}

		while (currentLineIndex < lines.size())
		{
			formattedLines.add(new ComparisonLine(lines.get(currentLineIndex++), LineType.SAME));
		}

		return formattedLines;
	}

	private void fireDiffCompleteEvent()
	{
		for (ComparisonModelListener listener : listeners)
			listener.diffCreationComplete();
	}

	private void fireDiffFailedEvent(String message)
	{
		for (ComparisonModelListener listener : listeners)
			listener.diffCreationFailed(message);
	}
}
